/*
 * drivers/input/keyboard/tcs9120.c
 *
 *
 */
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/gpio.h>
#include <linux/delay.h>


#define COMPATILE_NAME "tcs9120"

#define TCS9120_KEY_NUM 10
#define SDA_GPIO_NAME "sda_gpio"
#define SCL_GPIO_NAME "scl_gpio"
#define INT_GPIO_NAME "int_gpio"

/* DTS进行如下的配置

{
	tcs9120: tcs9120 {
		compatible = "tcs9120";
		sda_gpio = <&gpio1 1 GPIO_ACTIVE_HIGH>;
		scl_gpio = <&gpio1 2 GPIO_ACTIVE_HIGH>;
		int_gpio = <&gpio1 3 GPIO_ACTIVE_HIGH>;
	};
}
 */


struct tcs9120 {
	unsigned short keycode[TCS9120_KEY_NUM];
	struct device *dev;
	struct input_dev *input;
	struct delayed_work work;
	int irq;
	int scl_gpio;
	int sda_gpio;
	int int_gpio;
};

static unsigned short tcs8120_def_map[TCS9120_KEY_NUM] = {
	KEY_F1,KEY_F3,KEY_F4,KEY_F5,KEY_F6,KEY_F7,KEY_F8,KEY_F9,KEY_F10
};

static inline void send_clk(int gpio)
{
	gpio_set_value_cansleep(gpio, 0);
//	usleep_range(50, 80);
	gpio_set_value_cansleep(gpio, 1);
//	usleep_range(50, 80);
}

int tcs9120_read(struct tcs9120 *tc)
{
	int val = 0;
	int dat = 0, i=0;
	int retry = 10;
	dat = gpio_get_value_cansleep(tc->sda_gpio);
	if(dat){
		do{
			send_clk(tc->scl_gpio);
			dat=gpio_get_value_cansleep(tc->sda_gpio);
			dev_info(tc->dev, "%s start=%d, retry=%d\n", __func__, dat, retry);
		}while(dat && retry-- > 0);
		for(val=0, i=0; i<TCS9120_KEY_NUM; i++){
			send_clk(tc->scl_gpio);
			val <<= 1;
			dat = gpio_get_value_cansleep(tc->sda_gpio);
			if(dat){
				val |= 1;
			}
		}
		send_clk(tc->scl_gpio);
		dat = gpio_get_value_cansleep(tc->sda_gpio);
		dev_info(tc->dev, "%s data=%d, finish=%d\n", __func__, val, dat);
		if(dat==0){
			dev_err(tc->dev, "%s data invalid\n", __func__);
			val = 0;  // data invalid
		}
	}else{
		dev_err(tc->dev, "%s sda not high ,invalid\n", __func__);
	}
	return val;
}

extern void lamp_controller(int op);
// 16 32 64 128 256  -> lv1 ~lv5  4~8
// 512 OP_TOGGLE_NIGHT  9
// 8  OP_LAMP_EN     3
// 1  OP_TIME_REST   2
// 2  OP_DELAY_OFF   1
// 4 OP_TOGGLE_MODE   0
#define OP_LAMP_EN 0
#define OP_TOGGLE_NIGHT 1
#define OP_TOGGLE_MODE 2
#define OP_SET_LEVEL0 3
#define OP_SET_LEVEL1 4
#define OP_SET_LEVEL2 5
#define OP_SET_LEVEL3 6
#define OP_SET_LEVEL4 7
#define OP_SET_LEVEL5 8
#define OP_SET_LEVEL5 8
#define OP_MAX  (OP_SET_LEVEL5 + 1)

static void do_lamp(int val)
{
	static int mapper[] = {
		-1,
		-1,
		OP_TOGGLE_MODE,
		OP_LAMP_EN,
		OP_SET_LEVEL1,
		OP_SET_LEVEL2,
		OP_SET_LEVEL3,
		OP_SET_LEVEL4,
		OP_SET_LEVEL5,
		OP_TOGGLE_NIGHT
	};
	if(val >= ARRAY_SIZE(mapper) || val < 0){
		pr_err("%s bad value : %d\n", __func__, val);
		return;
	}
	lamp_controller(mapper[val]);
}

static void tcs9120_work(struct work_struct *work)
{
	int data = 0;
	int i, pressed, mask=0;
	int no_pressed = 1;
	static int key_status[TCS9120_KEY_NUM] = {0};  // 0 mean release
	struct tcs9120 *tcs9120 = container_of(work, struct tcs9120, work.work);

	data =  tcs9120_read(tcs9120);
	pr_info("%s read iic=%d\n", __func__, data);
	for(i=0, mask=1; i<TCS9120_KEY_NUM; i++, mask <<=1){
		pressed = (mask & data) ? 1 : 0;
		if(pressed){
			do_lamp(i);
		}
		if(pressed) no_pressed = 0;
		if(key_status[i] != pressed){
			key_status[i] = pressed;
			input_report_key(tcs9120->input, tcs9120->keycode[i], pressed);
			input_sync(tcs9120->input);
		}
	}

	// if(!no_pressed){
	// 	schedule_delayed_work(&tcs9120->work, msecs_to_jiffies(50));
	// }
}

static irqreturn_t tcs9120_interrupt(int irq, void *dev_id)
{
	struct platform_device *pdev = dev_id;
	struct tcs9120 *tcs9120 = platform_get_drvdata(pdev);
	schedule_delayed_work(&tcs9120->work, 0);  /* msecs_to_jiffies(20) */
	return IRQ_HANDLED;
};


static int st7710s_parse_dt(struct device *dev)
{
	int ret = 0;
	struct device_node *np = dev->of_node;
	struct tcs9120 *tcs9120  = dev_get_drvdata(dev);

	if(!np || !tcs9120){
		dev_err(dev, "no of_node found or tcs9120 NULL\n");
		return -ENODEV;
	}
	if((ret=of_get_named_gpio(np, SCL_GPIO_NAME, 0)) < 0){
		dev_err(dev, "of_get_named_gpio: %s fail, err=%d\n", SCL_GPIO_NAME, ret);
		if(ret == -EPROBE_DEFER){
			return -EPROBE_DEFER;
		}
	}
	tcs9120->scl_gpio = ret;

	if((ret=of_get_named_gpio(np, SDA_GPIO_NAME, 0)) < 0){
		dev_err(dev, "of_get_named_gpio: %s fail, err=%d\n", SDA_GPIO_NAME, ret);
		if(ret == -EPROBE_DEFER){
			return -EPROBE_DEFER;
		}
	}
	tcs9120->sda_gpio = ret;


	if((ret=of_get_named_gpio(np, INT_GPIO_NAME, 0)) < 0){
		dev_err(dev, "of_get_named_gpio: %s fail, err=%d\n", INT_GPIO_NAME, ret);
		if(ret == -EPROBE_DEFER){
			return -EPROBE_DEFER;
		}
	}
	tcs9120->int_gpio = ret;

	if(gpio_is_valid(tcs9120->scl_gpio )){
		gpio_request(tcs9120->scl_gpio, SCL_GPIO_NAME);
		gpio_direction_output(tcs9120->scl_gpio, 1);
	}
	if(gpio_is_valid(tcs9120->sda_gpio )){
		gpio_request(tcs9120->sda_gpio , SDA_GPIO_NAME);
		gpio_direction_input(tcs9120->sda_gpio);
	}

	tcs9120->irq = gpio_to_irq(tcs9120->int_gpio);

	return 0;
}





static int tcs9120_probe(struct platform_device *pdev)
{
	struct tcs9120 *tcs9120;
	struct input_dev *input_dev;
	int i, err;

	dev_err(&pdev->dev, "%s enter\n", __func__);
	tcs9120 = kzalloc(sizeof(struct tcs9120), GFP_KERNEL);
	input_dev = input_allocate_device();
	if (!tcs9120 || !input_dev) {
		err = -ENOMEM;
		goto fail1;
	}
	INIT_DELAYED_WORK(&tcs9120->work, tcs9120_work);
	platform_set_drvdata(pdev, tcs9120);
	tcs9120->dev = &pdev->dev;
	err = st7710s_parse_dt(&pdev->dev);
	if(err <0 ){
		return err;
	}
	memcpy(tcs9120->keycode, tcs8120_def_map, sizeof(tcs8120_def_map));

	tcs9120->input = input_dev;
	input_dev->name = "Touch keyboard";
	input_dev->phys = "tcs9120/input0";
	input_dev->keycode = tcs9120->keycode;
	input_dev->keycodesize = sizeof(unsigned short);
	input_dev->keycodemax = ARRAY_SIZE(tcs9120->keycode);
	input_dev->id.bustype = BUS_HOST;
	input_dev->dev.parent = &pdev->dev;

	__set_bit(EV_KEY, input_dev->evbit);
	for (i = 0; i < ARRAY_SIZE(tcs9120->keycode); i++)
		__set_bit(tcs9120->keycode[i], input_dev->keybit);
	__clear_bit(KEY_RESERVED, input_dev->keybit);

	err = request_threaded_irq(tcs9120->irq, NULL, tcs9120_interrupt,
				   IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING|IRQF_ONESHOT,
				   "tcs9120",
				   pdev);
	if (err) {
		printk(KERN_INFO "tcs9120: Unable to grab IRQ\n");
		goto fail1;
	}
	err = input_register_device(tcs9120->input);
	if (err)
		goto fail2;

	return 0;

 fail2:	/* IRQ, DEVICE, MEMORY */
	free_irq(tcs9120->irq, pdev);
 fail1:	/* DEVICE, MEMORY */
	platform_set_drvdata(pdev, NULL);
	input_free_device(input_dev);
	kfree(tcs9120);
	return err;
};

static int tcs9120_remove(struct platform_device *pdev)
{
	struct tcs9120 *tcs9120 = platform_get_drvdata(pdev);

	free_irq(tcs9120->irq, pdev);
	platform_set_drvdata(pdev, NULL);
	input_unregister_device(tcs9120->input);
	kfree(tcs9120);

	return 0;
}

static const struct of_device_id tcs9120_of_match[] = {
	{ .compatible = COMPATILE_NAME, },
	{},
};

static struct platform_driver tcs9120_driver = {
	.driver  = {
		.name    = "tcs9120",
		.owner	= THIS_MODULE,
		.of_match_table = of_match_ptr(tcs9120_of_match)
	 },
	.probe   = tcs9120_probe,
	.remove  = tcs9120_remove,
};

#if 0   // this make module unable to unload
// lsmod tcs9120 3395 0 [permanent], Live 0x00000000 (O)
module_platform_driver(tcs9120_driver);
#else
static int __init tcs9120_init(void)
{
	pr_info("Register tcs9120 driver init\n");
	if (platform_driver_register(&tcs9120_driver)) {
		pr_err("tcs9120: failed to register disp driver\n");
		return -ENODEV;
	}
	return 0;
}

static void __exit tcs9120_exit(void)
{

	platform_driver_unregister(&tcs9120_driver);
	pr_info("Unregister tcs9120 driver done\n");
}
//module_init(tcs9120_init);
late_initcall(tcs9120_init);
module_exit(tcs9120_exit);
#endif

MODULE_AUTHOR("tj<tj_wust@163.com>");
MODULE_LICENSE("GPL");